#include "main.h"
#include "assitant.h"
#include "mqtts.h"
#include "jc_sensors.h"
#include "jc_sensor_modules.h"

typedef JCSensor *(*JCSensorGenerator)(String &config);

JCSensor *jc_sensors[max_sensor_length];
byte jc_sensor_n = 0;

#define loads(m_name, m_class) #m_name
const char *sensor_tags[] = {modules};
#undef loads
#define loads(m_name, m_class) [](String &config) -> JCSensor * {JCSensor_##m_class *obj = new JCSensor_##m_class();if (obj->setup(config) < 0){delete obj;return NULL;}return obj; }
const JCSensorGenerator jc_sensor_generators[] = {modules};
#undef loads
#undef modules

static int cmd_sensor(int n, char *args[]);
static String sensor_read_at(int index);
static int search_sensor(String &s);
static int add_sensor(String &s);
static int remove_sensor(String &sensor_name);

void jc_sensors_init()
{
    new_cmd("sensor", cmd_sensor);
}
static String sensor_read_at(int index)
{
    var sensor_data = jc_sensors[index]->read();
    return sensor_data;
}

String jc_read_sensors()
{
    if (!jc_sensor_n)
    {
        return String("{}");
    }
    String data("{");
    int i;
    int ok_number = 0;
    for (i = 0; i < jc_sensor_n; i++)
    {
        var s = sensor_read_at(i);
        if (s.isEmpty())
            continue;
        s.concat(",");
        data.concat(s);
        ok_number++;
    }
    if (!ok_number)
    {
        return "{}";
    }
    var n = data.length();
    data.setCharAt(n - 1, '}');
    return data;
}

static int search_sensor(String &s)
{
    const int n = sizeof(sensor_tags) / sizeof(char *);
    for (int i = 0; i < n; i++)
    {
        if (s != sensor_tags[i])
            continue;
        return i;
    }
    return -1;
}

static int add_sensor(String &s)
{
    if (jc_sensor_n >= max_sensor_length)
        return -__LINE__;
    var ind = s.indexOf(':');
    int index;
    String config;
    if (ind < 0)
    {
        var tag = s;
        tag.toLowerCase();
        index = search_sensor(tag);
    }
    else
    {
        var tag = s.substring(0, ind);
        tag.toLowerCase();
        config = s.substring(ind + 1);
        index = search_sensor(tag);
    }
    if (index < 0)
    {
        out_("unknown sensor:");
        outs(s);
        return -__LINE__;
    }
    var sen_instance = jc_sensor_generators[index](config);
    assert_msg(sen_instance, "failed to add sensor");
    sen_instance->id = index;
    jc_sensors[jc_sensor_n++] = sen_instance;
    return index;
}

static int remove_sensor(String &sensor_name)
{
    var sensorTag_at = search_sensor(sensor_name);
    assert_msg(sensorTag_at >= 0, "bad sensorTag");
    int i = 0;
    for (; i < jc_sensor_n; i++)
    {
        if (jc_sensors[i]->id == sensorTag_at)
            break;
    }
    assert_msg(i < jc_sensor_n, "sensorTag not found");
    delete jc_sensors[i];
    jc_sensor_n--;
    if (!jc_sensor_n)
        return 0;
    jc_sensors[i] = jc_sensors[jc_sensor_n];
    return 0;
}

static int cmd_sensor(int n, char *args[])
{
    if (n < 2)
    {
        if (jc_sensor_n <= 0)
            return 0;
        for (int i = 0; i < jc_sensor_n; i++)
        {
            var at = jc_sensors[i]->id;
            out_(sensor_tags[at]);
            out_(',');
        }
        outs("");
        return 0;
    }
    String s(args[1]);
    if (s == "-h" || s == "--help" || s == "-help")
    {
        outs("sensor type:config  :to add a sensor");
        outs("sensor -r [sensorTag] :to remove specifically a  sensor or clear all sensor in default");
        out_("available types are:");
        for (int i = 0; i < sizeof(sensor_tags) / sizeof(const char *); i++)
        {
            out_(sensor_tags[i]);
            out_(",");
        }
        out_("\n");
        return 0;
    }
    if (s == "-r") // sensor -rm [sensorTag]
    {
        if (n <= 2) // remove all
        {
            for (int i = 0; i < jc_sensor_n; i++)
            {
                delete jc_sensors[i];
            }
            jc_sensor_n = 0;
            return 0;
        }
        String sensor_name(args[2]);
        return remove_sensor(sensor_name);
    }
    return add_sensor(s);
}

int cast_to_pin(const String &s)
{
    if (s.isEmpty())
        return -__LINE__;
    const int ps[] = {D0, D1, D2, D3, D4, D5, D6, D7, D8, D9};
    if (s[0] != 'D' && s[0] != 'd')
        return -__LINE__;
    var c = s[1];
    if (c < '0' || c > '9')
        return -__LINE__;
    return ps[c - '0'];
}